package requests

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

// Client 封装了 http.Client，可用于发送 HTTP 请求
type Client struct {
	httpClient *http.Client
}

// RequestOptions 定义了请求时可配置的参数
type RequestOptions struct {
	Headers     map[string]string // 请求头，如 {"User-Agent": "Golang-HttpClient"}
	Cookies     []*http.Cookie    // cookie 列表
	Body        interface{}       // 请求体数据（发送 POST 请求时使用，可为 string、[]byte、map[string]string、url.Values 等）
	ContentType string            // 请求体的 Content-Type，如 "application/json" 或 "application/x-www-form-urlencoded"
}

// NewRequestClient 创建一个新的 Client 实例，并可设置超时时间
func NewRequestClient(timeout time.Duration) *Client {
	return &Client{
		httpClient: &http.Client{
			Timeout: timeout,
		},
	}
}

// Get 发送 GET 请求，并返回响应字符串
func (c *Client) Get(urlStr string, options *RequestOptions) (string, error) {
	req, err := http.NewRequest("GET", urlStr, nil)
	if err != nil {
		return "", err
	}
	// 设置 header 和 cookie
	if options != nil {
		for k, v := range options.Headers {
			req.Header.Set(k, v)
		}
		for _, cookie := range options.Cookies {
			req.AddCookie(cookie)
		}
	}
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(data), nil
}

// Post 发送 POST 请求，并返回响应字符串
// 根据 ContentType 判断 Body 的处理方式：
//   - "application/json"：将 Body 进行 JSON 序列化
//   - "application/x-www-form-urlencoded"：将 Body 视为表单数据（支持 map[string]string 或 url.Values 类型）
//   - 其他情况：尝试将 Body 转换为字符串
func (c *Client) Post(urlStr string, options *RequestOptions) (string, error) {
	var bodyReader io.Reader
	if options != nil && options.Body != nil {
		switch options.ContentType {
		case "application/json":
			jsonData, err := json.Marshal(options.Body)
			if err != nil {
				return "", err
			}
			bodyReader = bytes.NewBuffer(jsonData)
		case "application/x-www-form-urlencoded":
			// 处理表单数据
			switch data := options.Body.(type) {
			case map[string]string:
				form := url.Values{}
				for key, value := range data {
					form.Set(key, value)
				}
				bodyReader = strings.NewReader(form.Encode())
			case url.Values:
				bodyReader = strings.NewReader(data.Encode())
			default:
				return "", errors.New("表单数据的 Body 必须为 map[string]string 或 url.Values 类型")
			}
		default:
			// 其他情况：优先判断是否为 string 或 []byte
			switch data := options.Body.(type) {
			case string:
				bodyReader = strings.NewReader(data)
			case []byte:
				bodyReader = bytes.NewBuffer(data)
			default:
				// 尝试 JSON 序列化
				jsonData, err := json.Marshal(options.Body)
				if err != nil {
					return "", err
				}
				bodyReader = bytes.NewBuffer(jsonData)
				// 如果未指定 ContentType，则默认为 application/json
				if options.ContentType == "" {
					options.ContentType = "application/json"
				}
			}
		}
	}

	req, err := http.NewRequest("POST", urlStr, bodyReader)
	if err != nil {
		return "", err
	}
	// 设置 Content-Type、header 和 cookie
	if options != nil {
		if options.ContentType != "" {
			req.Header.Set("Content-Type", options.ContentType)
		}
		for k, v := range options.Headers {
			req.Header.Set(k, v)
		}
		for _, cookie := range options.Cookies {
			req.AddCookie(cookie)
		}
	}
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(data), nil
}

// PostMultipart 使用 multipart/form-data 上传文件及其他表单字段
func (c *Client) PostMultipart(urlStr string, params map[string]string, paramName, path string, headers map[string]string) (string, error) {
	// 打开文件
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()

	// 创建一个缓冲区和 multipart writer
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// 添加文件字段
	part, err := writer.CreateFormFile(paramName, file.Name())
	if err != nil {
		return "", err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		return "", err
	}

	// 添加其他表单字段
	for key, val := range params {
		_ = writer.WriteField(key, val)
	}

	// 关闭 writer 写入结尾 boundary
	err = writer.Close()
	if err != nil {
		return "", err
	}

	// 创建请求
	req, err := http.NewRequest("POST", urlStr, &requestBody)
	if err != nil {
		return "", err
	}

	// 设置 Content-Type 为 multipart/form-data（包含 boundary）
	req.Header.Set("Content-Type", writer.FormDataContentType())

	// 设置其他自定义 header
	for k, v := range headers {
		req.Header.Set(k, v)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	// 读取响应数据
	respData, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(respData), nil
}
